/**
 * 
 */
package com.vssr.persist;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.srini.util.ConnectionUtil;
import com.vssr.persist.bean.Associate;
import com.vssr.persist.bean.Column;
import com.vssr.persist.bean.Table;



/**
 * @author Srini
 * 
 */
public class DataBaseWriter {
	private static Connection con;
	private StringBuffer query;
	private Map<String, Map> columnValueMap = new HashMap<String, Map>();
	private List hierarchy = new ArrayList();

	private DataBaseWriter() {
//		con = conn;
	}

	public static Object save(Object obj) {
		return new DataBaseWriter().saveObject(obj);
	}

	private Object saveObject(Object obj) {

		columnValueMap = new HashMap<String, Map>();
		hierarchy = new ArrayList();
		getDBColumns(obj);
		Map s = getInsertValues();
		String[] queries = new String[hierarchy.size()];
		int i = 0;
		System.out.println(hierarchy);
		for (Iterator iterator = hierarchy.iterator(); iterator.hasNext(); i++) {
			String type = (String) iterator.next();
			queries[i] = (String) s.get(type);
		}
		return execute(queries);
	
	}
	private Map getInsertValues() {
		Map queriesMap = new HashMap();
		Iterator<String> itr = columnValueMap.keySet().iterator();

		String[] colValues = new String[columnValueMap.size()];
		StringBuffer query = null;

		StringBuffer cols = null;
		StringBuffer vals = null;
		while (itr.hasNext()) {
			String key = itr.next();
			query = new StringBuffer("insert into ").append(key);
			cols = new StringBuffer(" (");
			vals = new StringBuffer(" (");
			Map obj = columnValueMap.get(key);
			obj.put("id", "DEFAULT");
			Iterator inItr = obj.keySet().iterator();
			while (inItr.hasNext()) {
				String inKey = (String) inItr.next();
				Object value = obj.get(inKey);
				cols.append(inKey);
				vals.append(value);
				if (inItr.hasNext()) {
					cols.append(",");
					vals.append(",");
				}
			}
			cols.append(")");
			vals.append(")");
			query.append(cols);
			query.append(" values ");
			query.append(vals);
			queriesMap.put(key, query.toString());

		}

		return queriesMap;
	}

	public static void update(Object obj) {
		new DataBaseWriter().updateObject(obj);
	}
	
	private void updateObject(Object obj) {


		Table table = obj.getClass().getAnnotation(
				Table.class);
		String tableName = table.name();

		query = new StringBuffer("update ");
		query.append(tableName).append(" set ");
		getDBColumns(obj);
		// query.append(getUpdateValues());
		// query.append(" where id=").append(columnValueMap.get("id"));
		// System.out.println(query);

		execute(getUpdateValues());
	
	}

	private Object execute(String... queries) {
		startTransaction();
		int generatedId = 0;
		ResultSet rs = null;
		try {
			if (queries != null && queries.length > 0) {
				PreparedStatement pstmt = null;
				for (int i = 0; i < queries.length; i++) {
					pstmt = con.prepareStatement(queries[i],
							Statement.RETURN_GENERATED_KEYS);
					System.out.println("Executing <b>" + queries[i]);
					if (i > 0 && generatedId > 0) {
						pstmt.setInt(1, generatedId);
					}
					pstmt.executeUpdate();
					rs = pstmt.getGeneratedKeys();
					if (rs != null) {
						while (rs.next()) {
							generatedId = rs.getInt(1);
						}
					}

				}
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		endTransaction();
		return generatedId;
	}

	private static void endTransaction() {
		try {
			con.commit();
			con.setAutoCommit(false);
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	private static void startTransaction() {

		try {
			con = new ConnectionUtil().getConnection();
			con.commit();
			con.setAutoCommit(false);
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	private static String referenceColumn = null;

	private Map getDBColumns(Object obj) {

		// columnValueMap = new HashMap();
		Table table = obj.getClass().getAnnotation(Table.class);
		String tableName = table.name();
		hierarchy.add(tableName);
		Field fields[] = obj.getClass().getDeclaredFields();
		Map params = new HashMap();
		Class[] paramTypes = null;
		Object[] objParams = null;
		Method method = null;
		try {
			for (Field field : fields) {
				if (field != null) {
					Column col = field.getAnnotation(Column.class);
					String methodPrefix = "";
					if (field.getType() == boolean.class) {
						methodPrefix = "is";
					} else {
						methodPrefix = "get";
					}
					String strMethodName = methodPrefix
							+ initCaps(field.getName());
					method = obj.getClass().getDeclaredMethod(strMethodName,
							paramTypes);
					Object o = method.invoke(obj, objParams);
					if (col != null) {
						if (field.getName().equalsIgnoreCase(referenceColumn)) {
							if (o != null && Integer.parseInt(o.toString()) > 0) {
								params.put(col.value(), o);
							} else {
								params.put(col.value(), "?");
							}
							referenceColumn = null;
						} else {
							params.put(col.value(), getFormattedData(o));
						}

						putParams(tableName, params);
					} else {
						Associate asso = field.getAnnotation(Associate.class);
						if (asso != null) {
							if (o != null) {
								referenceColumn = asso.reference();
								getDBColumns(o);
							}
						}
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return columnValueMap;
	}

	private void putParams(String obj, Map params) {
		if (columnValueMap.containsKey(obj)) {
			columnValueMap.get(obj).putAll(params);
		} else {
			columnValueMap.put(obj, params);
		}
	}

	private String[] getUpdateValues() {
		Iterator itr = columnValueMap.keySet().iterator();
		StringBuffer str = null;
		String[] queries = new String[columnValueMap.size()];
		int i = 0;
		while (itr.hasNext()) {

			String key = (String) itr.next();
			str = new StringBuffer("update ").append(key).append(" set ");
			Map obj = columnValueMap.get(key);
			Iterator inItr = obj.keySet().iterator();
			while (inItr.hasNext()) {
				String inKey = (String) inItr.next();
				str.append(inKey).append("=").append(obj.get(inKey));
				if (inItr.hasNext()) {
					str.append(",");
				}
			}
			str.append(" where id=").append(obj.get("id"));
			queries[i] = str.toString();
			i++;
		}

		return queries;
	}

	private String initCaps(String str) {
		// str
		StringBuffer result = new StringBuffer();
		result.append(str.substring(0, 1).toUpperCase()).append(
				str.substring(1, str.length()));

		return result.toString();
	}

	private static Object getFormattedData(Object obj) {
		if (obj == null) {
			return "''";
		}
		StringBuffer query = new StringBuffer();
		Class cls = obj.getClass();
		if (cls == Integer.class || cls == Long.class || cls == Double.class) {
			query.append(obj);
		} else if (cls == Date.class || cls == java.sql.Date.class) {
			query.append("toDate('yyyy-mm-dd', ");
			query.append("'").append(obj).append("')");
		} else {
			if (obj.toString().contains("'")) {
				obj = obj.toString().replace("'", "''");
			}
			query.append("'").append(obj).append("'");
		}

		return query;
	}

	public void delete(Object obj) {
		StringBuffer query = null;
		Table table = obj.getClass().getAnnotation(Table.class);
		if (table != null) {
			String tableName = table.name();
			query = new StringBuffer("delete from ");
			query.append(tableName);
			query.append(" where ");
			getDBColumns(obj);
			query.append(getUpdateValues());
		}
		Associate asso = obj.getClass().getAnnotation(Associate.class);

	}

}
